/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 * Copyright (C) 2010 Wolfgang Breyha <wolfgang.breyha@univie.ac.at>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gst/gst.h>

/* non-GST-specific stuff */
#include "gstv2esrc.h"
#include "v2esrc.h"
#include "frmgrab.h"

#include <string.h>

GST_DEBUG_CATEGORY_EXTERN (gst_v2esrc_debug);

#define GST_CAT_DEFAULT gst_v2esrc_debug

struct fourcc_list_struct fourcc_list[] = {
/* packed */
  {VTS_YUV, "YUY2", "YUY2", 16, V2U_GRABFRAME_FORMAT_YUY2},
  {VTS_YUV, "I420", "I420", 12, V2U_GRABFRAME_FORMAT_I420},
  {VTS_YUV, "YV12", "YV12", 12, V2U_GRABFRAME_FORMAT_YV12},
  {VTS_YUV, "NV12", "NV12", 12, V2U_GRABFRAME_FORMAT_NV12},
  {VTS_YUV, "Y800", "Y800", 8, V2U_GRABFRAME_FORMAT_Y8},
  {VTS_RGB, "RGB ", "ARGB8888", 32, V2U_GRABFRAME_FORMAT_ARGB32, 32,
      0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000},
  {VTS_RGB, "RGB ", "RGB888", 24, V2U_GRABFRAME_FORMAT_RGB24, 24,
      0x00ff0000, 0x0000ff00, 0x000000ff},
  {VTS_RGB, "RGB ", "BGR888", 24, V2U_GRABFRAME_FORMAT_BGR24, 24,
      0x000000ff, 0x0000ff00, 0x00ff0000},
  {VTS_RGB, "RGB ", "RGB565", 16, V2U_GRABFRAME_FORMAT_RGB16, 16,
      0x0000f800, 0x000007e0, 0x0000001f}
};

int n_fourccs = G_N_ELEMENTS (fourcc_list);

int v2e_init(Gstv2esrc * src)
{
        FrmGrabber* fg = NULL;
	src->fg = NULL;
	V2U_GrabFrame2 *test_frame;

        /* Initialize frmgrab library */
        FrmGrabNet_Init();

	fg = FrmGrabNet_OpenLocation(src->fg_host);
	if (fg != NULL) {
		V2U_VideoMode vm;

		FrmGrab_GetVideoMode(fg, &vm);
		src->fg = fg;
		src->fg_format = V2U_GRABFRAME_FORMAT_I420;
		src->vmwidth = vm.width;
		src->vmheight = vm.height;
		if ((src->vmwidth == 0) && (src->vmheight == 0))
		{
			GST_ELEMENT_ERROR (src, RESOURCE, TOO_LAZY, (NULL),
				("no signal detected by VGA2Ethernet!"));
			return FALSE;
		}
		FrmGrab_GetGrabParams(fg, src->fg_grabparam);
		if (src->ovrd_grabparam->flags != 0) {
			if (src->ovrd_grabparam->flags & V2U_FLAG_VALID_HSHIFT)
			{
				src->fg_grabparam->hshift = src->ovrd_grabparam->hshift;
				src->fg_grabparam->flags |= V2U_FLAG_VALID_HSHIFT;
			}
			if (src->ovrd_grabparam->flags & V2U_FLAG_VALID_VSHIFT)
			{
				src->fg_grabparam->vshift = src->ovrd_grabparam->vshift;
				src->fg_grabparam->flags |= V2U_FLAG_VALID_VSHIFT;
			}
			if (src->ovrd_grabparam->flags & V2U_FLAG_VALID_PHASE)
			{
				src->fg_grabparam->phase = src->ovrd_grabparam->phase;
				src->fg_grabparam->flags |= V2U_FLAG_VALID_PHASE;
			}
			if (src->ovrd_grabparam->flags & V2U_FLAG_VALID_OFFSETGAIN)
			{
				/* only update gain/offset if initialized from fg */
				if (src->fg_grabparam->flags & V2U_FLAG_VALID_OFFSETGAIN)
				{
					src->fg_grabparam->gain_r = src->ovrd_grabparam->gain_r;
					src->fg_grabparam->gain_g = src->ovrd_grabparam->gain_g;
					src->fg_grabparam->gain_b = src->ovrd_grabparam->gain_b;
					src->fg_grabparam->offset_r = src->ovrd_grabparam->offset_r;
					src->fg_grabparam->offset_g = src->ovrd_grabparam->offset_g;
					src->fg_grabparam->offset_b = src->ovrd_grabparam->offset_b;
					src->fg_grabparam->flags |= V2U_FLAG_VALID_OFFSETGAIN;
				}
			}
			if (src->ovrd_grabparam->flags & V2U_FLAG_VALID_PLLSHIFT)
			{
				src->fg_grabparam->pllshift = src->ovrd_grabparam->pllshift;
				src->fg_grabparam->flags |= V2U_FLAG_VALID_PLLSHIFT;
			}
			if (src->ovrd_grabparam->grab_flags_mask != 0)
			{
				V2U_INT32 delmask = (src->ovrd_grabparam->grab_flags_mask * -1) | src->ovrd_grabparam->grab_flags;
				src->fg_grabparam->grab_flags |= src->ovrd_grabparam->grab_flags;
				src->fg_grabparam->grab_flags &= delmask;
				src->fg_grabparam->grab_flags_mask |= src->ovrd_grabparam->grab_flags_mask;
			}
			FrmGrab_SetGrabParams(fg, src->fg_grabparam);
		}
		test_frame = FrmGrab_Frame(fg, src->fg_format, NULL);
		if (test_frame)
			FrmGrab_Release(fg, test_frame);
		else {
			GST_ELEMENT_ERROR (src, RESOURCE, TOO_LAZY, (NULL),
				("grabbing test frame failed. check grab parameters"));
			return FALSE;
		}
	} else {
		GST_ELEMENT_ERROR (src, RESOURCE, TOO_LAZY, (NULL),
			("connection to %s failed", src->fg_host));
		return FALSE;
	}
}

int v2e_start(Gstv2esrc * src)
{
	if (src->vmwidth < (src->cropw - src->cropx))
	{
		GST_ELEMENT_ERROR (src, RESOURCE, TOO_LAZY, (NULL),
			("crop window larger than horizontal resolution"));
		return FALSE;
	}
	if (src->vmheight < (src->croph - src->cropy))
	{
		GST_ELEMENT_ERROR (src, RESOURCE, TOO_LAZY, (NULL),
			("crop window larger than vertical resolution"));
		return FALSE;
	}
/*	int width = (src->cropw == -1) ? src->vmwidth : (src->cropw - src->cropx);
	int diffx = abs(src->width - width);
	if (diffx != 0)
	{
		GST_ELEMENT_ERROR (src, RESOURCE, TOO_LAZY, (NULL),
			("pipe width doesn't match grab width. difference: %d", diffx));
		return FALSE;
	}
	int height = (src->croph == -1) ? src->vmheight : (src->croph - src->cropy);
	int diffy = abs(src->height - height);
	if (diffy != 0)
	{
		GST_ELEMENT_ERROR (src, RESOURCE, TOO_LAZY, (NULL),
			("pipe height doesn't match grab height. difference: %d", diffy));
		return FALSE;
	} */
	src->fg_crop = NULL;
	if ((src->cropw != -1) && (src->croph != -1)) {
		src->fg_crop = g_malloc(sizeof(V2URect));
		src->fg_crop->x = src->cropx;
		src->fg_crop->y = src->cropy;
		src->fg_crop->width = src->cropw;
		src->fg_crop->height = src->croph;
	}
	FrmGrab_SetMaxFps(src->fg, src->fps);
	FrmGrab_Start(src->fg);

	return TRUE;
}

int v2e_stop(Gstv2esrc * src)
{
	FrmGrab_Stop(src->fg);
	if (src->fg_crop)
	{
		g_free(src->fg_crop);
		src->fg_crop = NULL;
	}
	return TRUE;
}

int v2e_deinit(Gstv2esrc * src)
{
	FrmGrab_Close(src->fg);
	src->fg = NULL;

        /* Deinitialize frmgrab library */
        FrmGrabNet_Deinit();

	return TRUE;
}

int v2e_grab_image (Gstv2esrc * src, GstBuffer *dest, int w, int h)
{
	V2U_GrabFrame2* f = FrmGrab_Frame(src->fg, src->fg_format, src->fg_crop);
	GST_DEBUG_OBJECT (src, "len: %d", f->imagelen);
	GST_BUFFER_SIZE(dest) = f->imagelen;
	GST_BUFFER_MALLOCDATA(dest) = g_malloc(f->imagelen);
	GST_BUFFER_DATA(dest) = GST_BUFFER_MALLOCDATA(dest);
	memcpy((void *)GST_BUFFER_DATA(dest), f->pixbuf, f->imagelen);
	FrmGrab_Release(src->fg, f);

	return TRUE;
}

GstStructure *
caps_get_structure (struct fourcc_list_struct * format)
{
  GstStructure *structure = NULL;
  unsigned int fourcc;
  int endianness;

  g_return_val_if_fail (format, NULL);

  fourcc =
      GST_MAKE_FOURCC (format->fourcc[0], format->fourcc[1], format->fourcc[2],
      format->fourcc[3]);

  switch (format->type) {
    case VTS_RGB:
      if (format->bitspp == 16) {
        endianness = G_BYTE_ORDER;
      } else {
        endianness = G_BIG_ENDIAN;
      }
      structure = gst_structure_new ("video/x-raw-rgb",
          "bpp", G_TYPE_INT, format->bitspp,
          "endianness", G_TYPE_INT, endianness,
          "depth", G_TYPE_INT, format->depth,
          "red_mask", G_TYPE_INT, format->red_mask,
          "green_mask", G_TYPE_INT, format->green_mask,
          "blue_mask", G_TYPE_INT, format->blue_mask, NULL);
      if (format->depth == 32 && format->alpha_mask > 0) {
        gst_structure_set (structure, "alpha_mask", G_TYPE_INT,
            format->alpha_mask, NULL);
      }
      break;
    case VTS_YUV:
    {
      structure = gst_structure_new ("video/x-raw-yuv",
          "format", GST_TYPE_FOURCC, fourcc, NULL);
    }
      break;
    default:
      g_assert_not_reached ();
      break;
  }
  return structure;
}

struct fourcc_list_struct *
caps_find_by_structure (const GstStructure * structure)
{
  int i;
  const char *media_type = gst_structure_get_name (structure);
  int ret;

  g_return_val_if_fail (structure, NULL);

  if (strcmp (media_type, "video/x-raw-yuv") == 0) {
    const char *s;
    int fourcc;
    guint32 format;

    ret = gst_structure_get_fourcc (structure, "format", &format);
    if (!ret)
      return NULL;
    for (i = 0; i < n_fourccs; i++) {
      s = fourcc_list[i].fourcc;
      /* g_print("testing %" GST_FOURCC_FORMAT " and %s\n", GST_FOURCC_ARGS(format), s); */
      fourcc = GST_MAKE_FOURCC (s[0], s[1], s[2], s[3]);
      if (fourcc_list[i].type == VTS_YUV && fourcc == format) {
        return fourcc_list + i;
      }
    }
  } else if (strcmp (media_type, "video/x-raw-rgb") == 0) {
    int red_mask;
    int green_mask;
    int blue_mask;
    int alpha_mask;
    int depth;
    int bpp;

    ret = gst_structure_get_int (structure, "red_mask", &red_mask);
    ret &= gst_structure_get_int (structure, "green_mask", &green_mask);
    ret &= gst_structure_get_int (structure, "blue_mask", &blue_mask);
    ret &= gst_structure_get_int (structure, "depth", &depth);
    ret &= gst_structure_get_int (structure, "bpp", &bpp);

    if (depth == 32) {
      ret &= gst_structure_get_int (structure, "alpha_mask", &alpha_mask);
      ret &= (alpha_mask != 0);
    } else {
      alpha_mask = 0;
    }

    if (!ret) {
      GST_WARNING ("incomplete caps structure: %" GST_PTR_FORMAT, structure);
      return NULL;
    }
    for (i = 0; i < n_fourccs; i++) {
      if (fourcc_list[i].type == VTS_RGB &&
          fourcc_list[i].red_mask == red_mask &&
          fourcc_list[i].green_mask == green_mask &&
          fourcc_list[i].blue_mask == blue_mask &&
          (alpha_mask == 0 || fourcc_list[i].alpha_mask == alpha_mask) &&
          fourcc_list[i].depth == depth && fourcc_list[i].bitspp == bpp) {
        return fourcc_list + i;
      }
    }
    return NULL;
  }
  g_critical ("format not found for media type %s", media_type);

  return NULL;
}

